package meta

import (
	"context"
	"fmt"
	apiv1 "gomod.alauda.cn/mysql-mgr-operator/api/v1"
	"k8s.io/apimachinery/pkg/fields"
	toolscache "k8s.io/client-go/tools/cache"
	"satellite/app/common"
	"satellite/pkg/mgrclient"
	"satellite/pkg/uix/meta"
	"satellite/pkg/util"
	"sigs.k8s.io/controller-runtime/pkg/cache"
	"time"
)

type ShowOption struct {
	EnableWatch bool
}

type Show struct {
	*ShowOption
	*Base
}

func NewShow(opt *ShowOption) *Show {
	return &Show{
		ShowOption: opt,
	}
}

func (s *Show) WithBase(base *Base) *Show {
	s.Base = base
	return s
}

func (s *Show) Run() error {
	if !s.EnableWatch {
		return s.showOnce()
	}
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	dataChan, err := s.watchMeta(ctx)
	if err != nil {
		return err
	}
	return s.List(ctx, dataChan)
}

func (s *Show) showOnce() error {
	metaCr, err := s.getMeta()
	if err != nil {
		return err
	}
	util.Println(metaCr.Status)
	return nil
}

func (s *Show) watchMeta(ctx context.Context) (chan *apiv1.MySQLMeta, error) {
	stopCh := make(chan struct{})
	defer close(stopCh)
	dataCh := make(chan *apiv1.MySQLMeta, 1)
	controller, err := cache.New(mgrclient.Config(), cache.Options{
		Scheme:    mgrclient.Scheme(),
		Namespace: s.Namespace,
		DefaultSelector: cache.ObjectSelector{
			Field: fields.OneTermEqualSelector("metadata.name", s.metaName()),
		},
	})
	if err != nil {
		return nil, err
	}
	informer, err := controller.GetInformer(ctx, new(apiv1.MySQLMeta))
	if err != nil {
		return nil, err
	}
	handler := toolscache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			if metaCr, ok := obj.(*apiv1.MySQLMeta); ok {
				dataCh <- metaCr
			}
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			if metaCr, ok := newObj.(*apiv1.MySQLMeta); ok {
				dataCh <- metaCr
			}
		},
	}
	_, err = informer.AddEventHandler(handler)
	if err != nil {
		return nil, err
	}
	go controller.Start(ctx)
	return dataCh, nil
}

func (s *Show) ShowByHtml() error {
	//e := gin.Default()
	//e.GET("/meta", func(c *gin.Context) {
	//	t, err := template.ParseFiles("")
	//	if err != nil {
	//		return
	//	}
	//	c.HTML(200,"meta",t.)
	//})
	return nil
}

func (s *Show) List(ctx context.Context, dataChan chan *apiv1.MySQLMeta) error {
	u := meta.NewMetaUi(ctx, "Meta", dataChan)
	err := u.Show(backupInfoHandler, timeRangeHandler)
	if err != nil {
		return err
	}
	return nil
}

func (s *Show) parseBackupInfo(dataChan chan *apiv1.MySQLMeta) chan []string {
	resultChan := make(chan []string, 1)
	go func() {
		for c := range dataChan {
			status := c.Status
			rows := make([]string, 0, len(status.BackupInfos))
			for i, info := range status.BackupInfos {
				t := "^"
				if info.Spec.Type == apiv1.TypeOfBackupFull {
					t = "#"
				}
				state := ""
				colorStr := ""
				switch info.Status.State {
				case common.StateOfRunning:
					state = ">"
					colorStr = "(fg:blue)"
				case common.StateOfFailed:
					state = "x"
					colorStr = "(fg:red)"
				case common.StateOfSucceeded:
					state = "o"
					colorStr += "(fg:green)"
				case common.StateOfDeleted:
					state = "-"
					colorStr += "(fg:black)"
				case common.StateOfUnknown:
					state = "?"
					colorStr = "(fg:yellow)"
				}
				str := fmt.Sprintf("%-5d %v [%v]%v %v", i, t, state, colorStr, info.Name)
				rows = append(rows, str)
			}
			resultChan <- rows
		}
	}()
	return resultChan
}

func backupInfoHandler(metaCr *apiv1.MySQLMeta) []string {
	rows := make([]string, 0, len(metaCr.Status.BackupInfos))
	for i, info := range metaCr.Status.BackupInfos {
		t := "^"
		if info.Spec.Type == apiv1.TypeOfBackupFull {
			t = "#"
		}
		state := ""
		colorStr := ""
		switch info.Status.State {
		case common.StateOfRunning:
			state = ">"
			colorStr = "(fg:blue)"
		case common.StateOfFailed:
			state = "x"
			colorStr = "(fg:red)"
		case common.StateOfSucceeded:
			state = "o"
			colorStr += "(fg:green)"
		case common.StateOfDeleted:
			state = "-"
			colorStr += "(fg:black)"
		case common.StateOfUnknown:
			state = "?"
			colorStr = "(fg:yellow)"
		}
		str := fmt.Sprintf("%-5d %v [%v]%v %v", i, t, state, colorStr, info.Name)
		rows = append(rows, str)
	}
	return rows
}

func timeRangeHandler(metaCr *apiv1.MySQLMeta) []string {
	rows := make([]string, 0, len(metaCr.Status.ValidTimeRanges)+1)
	rows = append(rows, fmt.Sprintf("version: %v", metaCr.Status.Version))
	for i, timeRange := range metaCr.Status.ValidTimeRanges {
		str := fmt.Sprintf("%-5d %v -> %v (%v -> %v)",
			i,
			timeRange.StartTime.Format(time.RFC3339),
			timeRange.StopTime.Format(time.RFC3339),
			timeRange.StartIndex,
			timeRange.StopIndex,
		)
		rows = append(rows, str)
	}
	return rows
}
